﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Net;
using System.Text;
using System.Windows.Forms;
using System.Xml;

using ICSharpCode.WinFormsUI.Controls;
using ICSharpCode.WinFormsUI.Forms;

namespace Autoupgrade
{
    public partial class MainForm : BaseForm
    {        
        private double total;
        private double downloaded;
        private int finished;
        private string url;
        private string save;
        private string path;
        private string handler;
        private string launcher;
        private string startInfo;
        private string productName;       
        private string localVersion;
        private string[] args_array;
        private bool _canceled;
        private bool _expanded;
        private bool _autograde;

        public bool Autograde
        {
            get
            {
                return this._autograde;
            }
            set
            {
                this._autograde = value;
            }
        }

        public MainForm()
        {
            InitializeComponent();
            InitializeControls();
        }

        public MainForm(ICSharpCode.WinFormsUI.Theme.WinFormsUIThemeBase Theme)
            : this()
        {
            LoadXTheme(Theme);
        }

        protected override void OnClosed(EventArgs e)
        {
            try
            {
                this._canceled = true;
                base.OnClosed(e);
            }
            catch
            {
                Environment.Exit(0);
            }
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            this._canceled = true;
            base.OnClosing(e);            
            Launcher("close");
        }

        public void InitializeControls()
        {
            this.launcher = "launcher.exe";
            this.save = AppDomain.CurrentDomain.BaseDirectory;           
            this.SystemMenu = this.SystemContextMenu;            
            this.XTheme = new ICSharpCode.WinFormsUI.Theme.ThemeVS2012();
            this.ToolBar.BorderColor = this.XTheme.FormBorderOutterColor;
            this.ShowIconMenu = true;
            this.BtnApply.Foused = true;
            this.lblInfo.AutoSize = false;            
            this.LoadFormStyle();
            this.CenterToScreen();
        }

        private void InitializeSettings()
        {            
            try
            {
                this.lblTitle.Text = "正在检查版本信息,请稍候...";
                this.lblInfo.Text = "正在检查版本信息,请稍候...";
                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.Load(AppDomain.CurrentDomain.BaseDirectory + "AssistantConfig.config");
                xmlDocument.SelectSingleNode("//Version");
                this.localVersion = xmlDocument.SelectSingleNode("//value").InnerText;
                this.productName = xmlDocument.SelectSingleNode("//productname").InnerText;
                this.url = xmlDocument.SelectSingleNode("//url").InnerText;
                this.handler = xmlDocument.SelectSingleNode("//handler").InnerText;
                this.path = xmlDocument.SelectSingleNode("//path").InnerText;
                this.startInfo = xmlDocument.SelectSingleNode("//startinfo").InnerText;
                this.lblTitle.Text = "当前版本： " + this.localVersion;
                this.lblResult.Text = string.Concat("共计 ", 0, " 个更新文件");
            }
            catch (Exception ex)
            {
                this._canceled = true;
                this.lblInfo.Text = ex.Message;
            }
        }

        private void LoadFormStyle()
        {
            if (!_expanded)
            {
                _expanded = true;
                lbldetailLabel.Visible = false;
                lblResult.Visible = false;
                lblResultLabel.Visible = false;
                StateList.Visible = false;
                this.MinimumSize = new Size(400, 200);
                this.Size = new Size(this.Width, 200);// 400 200
                this.btnExpand.BackgroundImage = Autoupgrade.Properties.Resources.down;
            }
            else
            {
                _expanded = false;
                lbldetailLabel.Visible = true;
                lblResult.Visible = true;
                lblResultLabel.Visible = true;
                StateList.Visible = true;
                this.MinimumSize = new Size(400, 340);
                this.Size = new Size(this.Width, 340); //400 486
                this.btnExpand.BackgroundImage = Autoupgrade.Properties.Resources.up;
            }
        }

        private void LoadXTheme(ICSharpCode.WinFormsUI.Theme.WinFormsUIThemeBase Theme)
        {
            if (Theme.GetType() == typeof(ICSharpCode.WinFormsUI.Theme.ThemeVS2012))
            {
                this.BtnApply.Radius = 0;
                this.BtnCancel.Radius = 0;
            }
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            this.InitializeSettings();
            this.Start();
        }

        private void btnExpand_Click(object sender, EventArgs e)
        {
            LoadFormStyle();
        }

        private HttpWebResponse RequestVersions(string url, params object[] paramters)
        {
            string[] strArrays;
            HttpWebResponse response = null;
            if (!this._canceled)
            {
                string str = url;
                if (!paramters[0].ToString().Trim().Contains(".xml"))
                {
                    strArrays = new string[] { url, "/", paramters[0].ToString(), "?arg=", paramters[1].ToString() };
                    str = string.Concat(strArrays);
                }
                else
                {
                    strArrays = new string[] { url, "/Versions/", paramters[1].ToString(), "/", paramters[0].ToString() };
                    str = string.Concat(strArrays);
                }
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(str);
                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
                request.Credentials = CredentialCache.DefaultCredentials;
                response = (HttpWebResponse)request.GetResponse();
            }
            return response;
        }

        private void ReportProgress(double value, double total)
        {
            if (!this._canceled)
            {
                this.ProgressBar.Invoke(new MethodInvoker(() =>
                {
                    this.ProgressBar.Value = (total == 0 ? 0 : (int)(value / total * 100));
                    this.lblState.Text = (total == 0 ? string.Concat(0.ToString("N1"), " %") : string.Concat((value / total * 100).ToString("N1"), " %"));
                }));
            }
        }

        private void UpdateHandle(NDownListItem item, string url, string path, string name, string save)
        {
            if (!this._canceled)
            {
                this.ProgressBar.Invoke(new MethodInvoker(() =>
                {
                    this.StateList.SelectedItem = item;
                    this.lblInfo.Text = string.Concat("正在下载 ", name);
                }));
                string str = this.DownFiles(item.ProgressBar, item.StateLabel, url, string.Concat(path, "\\", name), save, item.Label);

                if (name == "Autoupgrade.exe" ||
                    name == "ICSharpCode.WinFormsUI.dll" ||
                    name == "ICSharpCode.WinFormsUI.Controls.dll")
                {
                    name = name + ".temp";
                }
                
                if (File.Exists(string.Concat(save, name)))
                {
                    string label = item.Label;
                    if ((string.IsNullOrEmpty(label) ? false : label.Length == 14))
                    {
                        string str1 = label.Substring(0, 4);
                        string str2 = label.Substring(4, 2);
                        string str3 = label.Substring(6, 2);
                        string str4 = label.Substring(8, 2);
                        string str5 = label.Substring(10, 2);
                        string str6 = label.Substring(12, 2);
                        object[] objArray = new object[] { str1, str2, str3, str4, str5, str6 };
                        label = string.Format("{0}-{1}-{2} {3}:{4}:{5}", objArray);
                        File.SetCreationTime(string.Concat(save, name), Convert.ToDateTime(label));
                        File.SetLastWriteTime(string.Concat(save, name), Convert.ToDateTime(label));
                    }
                }
                this.ProgressBar.Invoke(new MethodInvoker(() => this.lblInfo.Text = str));                 
                this.finished = this.finished + 1;
            }
        }

        private string DownFiles(ICSharpCode.WinFormsUI.Controls.DownList.ProgressBar prog, ICSharpCode.WinFormsUI.Controls.DownList.StateLabel lbl, string url, string file, string downPath, string label)
        {
            string message = string.Empty;
            Uri requestUrl = new Uri(string.Concat(url, "/", file.Replace('\\', '/').TrimStart('/').Replace("//", "/")));
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
            request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
            request.Credentials = CredentialCache.DefaultCredentials;            
            try
            {
                if (prog != null)
                {
                    if (!this._canceled)
                    {
                        this.ProgressBar.Invoke(new MethodInvoker(() => prog.Value = 0));
                        this.ProgressBar.Invoke(new MethodInvoker(() => lbl.Text = string.Concat(0, " %")));
                    }
                    else
                    {
                        message = "操作被取消";
                        return message;
                    }
                }               
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Stream responseStream = response.GetResponseStream();
                long contentLength = response.ContentLength;
                string fileName = file.Replace(this.path, "").Trim('\\');                
                string downFile = string.Concat(downPath, fileName);

                if (fileName == "ICSharpCode.WinFormsUI.dll" ||
                    fileName == "ICSharpCode.WinFormsUI.Controls.dll")
                {
                    downFile = string.Concat(downPath, fileName + ".temp");
                }

                System.IO.DirectoryInfo directoryInfo = new DirectoryInfo(downFile.Substring(0, downFile.LastIndexOf("\\")));
                if (!directoryInfo.Exists)
                {
                    directoryInfo.Create();
                }
                FileStream fileStream = File.Open(downFile, FileMode.Create, FileAccess.ReadWrite);
                try
                {
                    if (contentLength >= (long)0)
                    {
                        float single = 0f;
                        byte[] numArray = new byte[contentLength];
                        int fileLength = responseStream.Read(numArray, 0, (int)contentLength);
                        long totalLength = (long)fileLength;
                        this.downloaded = this.downloaded + (double)((long)fileLength);
                        while (fileLength != 0)
                        {
                            if (!this._canceled)
                            {
                                fileStream.Write(numArray, 0, fileLength);
                                fileLength = responseStream.Read(numArray, 0, (int)contentLength);
                                single = (float)totalLength / (float)contentLength * 100f;
                                totalLength = totalLength + (long)fileLength;
                                if (lbl != null)
                                {
                                    if (single >= 100f)
                                    {
                                        this.ProgressBar.Invoke(new MethodInvoker(() => lbl.Text = string.Concat(single.ToString("N0"), " %")));
                                    }
                                    else
                                    {
                                        this.ProgressBar.Invoke(new MethodInvoker(() => lbl.Text = string.Concat(single.ToString("N2"), " %")));
                                    }
                                }
                                if (prog != null)
                                {
                                    this.ProgressBar.Invoke(new MethodInvoker(() => prog.Value = (int)single));
                                }
                                Application.DoEvents();
                                this.downloaded = this.downloaded + (double)((long)fileLength);
                                this.ReportProgress(this.downloaded, this.total);
                            }
                            else
                            {
                                message = "操作被取消";
                                return message;
                            }
                        }
                        fileStream.Flush();
                        fileStream.Close();
                    }
                    else
                    {
                        contentLength = (long)1;
                        message = "文件下载失败?可能不支持该文件格式";
                        return message;
                    }
                }
                finally
                {
                    if (fileStream != null)
                    {
                        ((IDisposable)fileStream).Dispose();
                    }
                }
                response.Close();
                response.Dispose();
                message = "download success";
            }
            catch (Exception ex)
            {                 
                try
                {
                    request.Abort();
                }
                catch
                {
                }
                this._canceled = true;
                message = ex.Message;
            }
            return message;
        }

        public void Start()
        {              
            int count = 0;
            string innerText = "";
            if (this.Autograde)
            {
                this.total = 0;

                DateTime startTime = DateTime.Now;
                DateTime endTime = DateTime.Now;

                this.launcher = "";
                this.StateList.Items.Clear(); 
              
                System.Threading.Tasks.Task.Run(() => {                    
                    try
                    {
                        //获取服务端版本信息
                        HttpWebResponse httpWebResponse = this.RequestVersions(this.url, new object[] { this.handler, this.productName });
                        XmlDocument xmlDocument = new XmlDocument();
                        using (Stream responseStream = httpWebResponse.GetResponseStream())
                        {
                            xmlDocument.Load(responseStream);
                        }                        
                        httpWebResponse.Close();
                        httpWebResponse.Dispose();

                        innerText = xmlDocument.SelectSingleNode("//Value").InnerText;
                        this.lblTitle.Invoke(new MethodInvoker(() => this.lblTitle.Text = string.Concat("最新版本 ", innerText, " 等待更新")));
                        System.Threading.Thread.Sleep(600);
                        this.ProgressBar.Invoke(new MethodInvoker(() => {
                            this.ProgressBar.Value = 0;
                            this.lblState.Text = "00.0 %";
                            this.lblTitle.Text = string.Concat("最新版本 ", innerText, " 正在更新中..");
                            this.lblInfo.Text = "正在计算更新文件包大小...";
                        }));
                        System.Threading.Thread.Sleep(600);
                        XmlNode xmlNodes = xmlDocument.SelectSingleNode("//Items");
                        for (int i = 0; i < xmlNodes.ChildNodes.Count; i++)
                        {                             
                            XmlNode node = xmlNodes.ChildNodes[i];
                            string name = node.Attributes["name"].Value;
                            string type = node.Attributes["type"].Value;
                            string file = node.Attributes["file"].Value;
                            string version = node.Attributes["version"].Value;
                            string remotefiletime = node.Attributes["version"].Value;
                            string localfiletime = string.Empty;
                            FileInfo fileInfo = new FileInfo(string.Concat(this.save, file));
                            if (fileInfo.Exists)
                            {
                                localfiletime = fileInfo.LastWriteTime.ToString("yyyyMMddHHmmss");
                            }
                            if (!remotefiletime.Equals(localfiletime))
                            {
                                this.total = this.total + (double)Convert.ToInt64(node.Attributes["size"].Value);
                                this.lblInfo.Invoke(new MethodInvoker(() => {
                                    this.StateList.Items.Add(new NDownListItem(name, file) { Label = remotefiletime }, true);  
                                    this.lblInfo.Text = string.Concat("文件共计 ", (this.total / 1024).ToString("N2"), " KB");
                                }));
                            }
                        }
                        count = this.StateList.Items.Count;
                        this.lblResult.Invoke(new MethodInvoker(() => this.lblResult.Text = string.Concat("共计 ", count, " 个更新文件")));
                    }
                    catch (Exception ex)
                    {                         
                        this.ProgressBar.Invoke(new MethodInvoker(() => {
                            this._canceled = true;
                            this.lblInfo.Text = ex.Message;
                        }));
                        return;
                    }
                    System.Threading.Thread.Sleep(400);
                    if (!this._canceled)
                    {
                        startTime = DateTime.Now;
                        if (this.StateList.Items.Count > 0)
                        {
                            foreach (NDownListItem item in this.StateList.Items)
                            {
                                if (!this._canceled)
                                {
                                    this.UpdateHandle(item, this.url, this.path, item.Text, this.save);
                                }
                            }
                        }
                        else
                        {
                            this.ProgressBar.Invoke(new MethodInvoker(() =>
                            {
                                this.ProgressBar.Value = 100;                                 
                            }));
                        }
                        endTime = DateTime.Now;

                        if (System.IO.File.Exists("launcher.exe"))
                        {
                            if (System.IO.File.Exists("ICSharpCode.WinFormsUI.Controls.dll.temp") ||
                                 System.IO.File.Exists("ICSharpCode.WinFormsUI.dll.temp"))
                                this.launcher = "launcher.exe";
                        }

                    }
                }).ContinueWith((System.Threading.Tasks.Task task) => {
                    if (!this._canceled)
                    {
                        TimeSpan timeSpan = endTime - startTime;
                        double speed = timeSpan.TotalMilliseconds == 0 ? this.total / 1024 / 1024 * 1000 : this.total / 1024 / 1024 / timeSpan.TotalMilliseconds * 1000;
                        this.Finished(innerText, speed);
                    }
                });
            }
        }

        private void Suspend()
        {
            if (this.finished != -1)
            {
                if (this._canceled)
                {
                    this._canceled = false;
                    this.BtnApply.Text = "暂停";
                    this.InitializeSettings();
                    this.Start();
                }                
                else
                {
                    this._canceled = true;
                    this.downloaded = 0;
                    this.BtnApply.Text = "继续";
                }
            }             
        }

        private void Cancel()
        {
            this._canceled = true;
            this.Launcher("cancel");          
        }

        private void Finished(string version, double speed)
        {
            this.lblInfo.Invoke(new MethodInvoker(() =>
            {
                this.finished = -1;
                this.lblState.Text = "100 %";
                this.BtnApply.Text = "完成";
                this.lblTitle.Text = string.Concat("最新版本 ", version, " 更新完成");
                this.lblInfo.Text = string.Concat("成功升级到 ", version, " 最新版本");
            }));
            try
            {
                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.Load(string.Concat(AppDomain.CurrentDomain.BaseDirectory, "AssistantConfig.config"));
                ((XmlElement)xmlDocument.SelectSingleNode("//value")).InnerText = version;
                xmlDocument.Save(string.Concat(AppDomain.CurrentDomain.BaseDirectory, "AssistantConfig.config"));
            }
            catch
            {
                System.Threading.Thread.Sleep(100);
                this.Finished(version, speed);
            }
            this.lblResult.Invoke(new MethodInvoker(() =>
            {                 
                object[] count = new object[] { 
                    "共更新 ", 
                    this.StateList.Items.Count, 
                    " 个文件 ", 
                    (this.total / 1024 / 1024).ToString("N2"),
                    " mb  平均速度：", 
                    speed.ToString("N2"), 
                    " mb/s" };                 
                this.lblResult.Text = string.Concat(count);
            }));
        }

        private void Launcher(string command)
        {            
            if (command == "cancel")
            {
                Environment.Exit(0);
            }
            if (!string.IsNullOrEmpty(this.launcher))
            {
                command = "launching";
                Process.Start(this.launcher, this.startInfo + (this.args_array == null ? "" : "," + string.Join(",", this.args_array)));
                //if (command != "close")
                //    Environment.Exit(0); //base.Close();
                Application.Exit();
            }
            else 
            {
                if (command == "restart")
                    if (command != "close")
                    {
                        Process process = (this.args_array == null ? Process.Start(this.startInfo) :
                            Process.Start(this.startInfo, string.Join(",", this.args_array)));
                        base.Close();
                    }   
            }
        }

        private void BtnApply_Click(object sender, EventArgs e)
        {
            if (this.finished != -1)
            {
                this.Suspend();
            }           
            else
            {
                if (!File.Exists(this.startInfo))
                {
                    MessageBox.Show("文件不存在");
                }
                else
                {
                    Launcher("restart");
                }               
            }
        }

        private void BtnCancel_Click(object sender, EventArgs e)
        {
            this.Cancel();
        }

        private void toolStripMenuItemSettings_Click(object sender, EventArgs e)
        {
            SettingForm aboutForm = new SettingForm("设置", this.XTheme);
            if (DialogResult.OK.Equals(aboutForm.ShowDialog(this)))
            {

            }
        }

        private void toolStripMenuItemAbout_Click(object sender, EventArgs e)
        {
            AboutForm aboutForm = new AboutForm("关于", this.XTheme);
            if (DialogResult.OK.Equals(aboutForm.ShowDialog(this)))
            {

            }
        }

    }
}
